import { Button, Card, Divider, Input, Tabs, TabsProps } from "antd";
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";



const LoginForm = forwardRef((props: any, ref: React.ForwardedRef<{ reset: (flag: boolean) => void; }>) => {
    const { name } = props;
    const [formData, setFormData] = useState({
        username: '',
        password: ''
    });
    useImperativeHandle(ref, () => ({
        // 重置表单内容
        reset: () => {
            setFormData({
                username: '',
                password: ''
            })
        },
    }));
    return <div className="mt-12px font-bold">
        <h2>{name}</h2>
        <div>
            <div className="mt-12px">
                用户名：<Input type="text" value={formData.username} onChange={(e) => {
                    setFormData({
                        ...formData,
                        username: e.target.value
                    })
                }} />
            </div>
            <div className="mt-12px">
                密码：<Input type="password" value={formData.password} onChange={(e) => {
                    setFormData({
                        ...formData,
                        password: e.target.value
                    })
                }} />
            </div>
        </div>
    </div>
})
const DemoRef = () => {
    const ref = useRef<any>(null)
    //闭包使用：计时器显示
    const [duration, setDuration] = useState(0);
    const durationRef = useRef(duration);
    //获取DOM
    const durationRefDOM = useRef<any>(null);
    // 渲染次数
    const renderCountRef = useRef(1);
    const renderValueRef = useRef<any>(0);
    const [renderValue, setRenderValue] = useState<any>(0);
    // 子组件ref
    const childRef = useRef<any>(null);

    useEffect(() => {
        renderCountRef.current = renderCountRef.current + 1;

        console.log(`🔄 组件第 ${renderCountRef.current + 1} 次渲染`);
    });

    return <div className="flex w-full">
        <Card title="useRef的使用" hoverable={true}>
            <div className="mt-12px flex flex-col max-w-200px">
                <Button onClick={() => {
                    setInterval(() => {
                        setDuration(durationRef.current + 1);
                        durationRef.current = durationRef.current + 1;
                    }, 1000);
                }} type="primary">开始计时</Button>
                <div ref={durationRefDOM}>当前计时：{duration}</div>
                <div className="mt-12px"></div>
                <Button onClick={() => {
                    clearInterval(ref.current);
                }} type="primary">获取DOM元素</Button>
                <div className="mt-12px font-bold">当前计时DOM元素内容：{durationRefDOM.current?.innerHTML}</div>
            </div>
        </Card>
        <Card title="useRef的渲染机制" className="ml-12px" hoverable={true}>
            <div className="mt-12px max-w-200px text-[#e74c3c] bg-[#fffacd] p-4 text-[18px] font-bold flex w-full flex-row w-400px">
                组件渲染此时：<span className="font-bold">{renderCountRef.current}</span>
            </div>
            <Button onClick={() => {
                renderValueRef.current = renderCountRef.current + 1;
            }} className="mt-12px" type="primary">更新Ref值</Button>
            <div className="mt-12px font-bold mb-12px">当前Ref值：{renderValueRef.current}(点击虽然新增了，但是组件没有重新渲染，导致此处仍然时老的值)</div>
            <Button onClick={() => {
                setRenderValue((pre: number) => pre + 1);
            }} type="primary">更新State值</Button>
            <div className="mt-12px font-bold">当前State值：{renderValue}(点击会触发组件重新渲染，导致此处的值会更新)</div>
            <div className="flex flex-row items-center mt-12px">
                <Button onClick={() => {
                    renderValueRef.current = renderCountRef.current + 1;
                    alert("ref值立即更新:" + renderValueRef.current)
                }} type="primary" className="mr-12px">Ref值立即更新</Button>
                <Button onClick={() => {
                    setRenderValue((pre: number) => pre + 1);
                    alert("state值不会立即更新:" + renderValue)
                }} type="primary">State值不会立即更新</Button>
            </div>
        </Card>
        <Card title="useRef和useForwardRef的组合" className="ml-12px" hoverable={true}>
            <Button onClick={() => {
                childRef.current?.reset();
            }} type="primary">重置</Button>
            <Divider></Divider>
            <LoginForm name="登录表单" ref={childRef} />
        </Card>

    </div>
}

// 常用的hook useState、useEffect、useCallback、useMemo
const HookDemo = () => {
    const [count, setCount] = useState(0)
    const [name, setName] = useState('')
    const [age, setAge] = useState(12)

    // 多次调用，每次都会重新渲染
    const handleClickCount = () => {
        setCount(count + 1);
        setCount(count + 1);
        setCount(count + 1);
        setCount(count + 1);
    }
    // 更新name
    const handleClickName = () => {
        setName((pre) => pre + 'xiaoming,')
        setName((pre) => pre + 'xiaohong,')
        setName((pre) => pre + 'xiaojun,')
    }
    return <div className="flex flex-row mt-12px">
        <Card title="useState的使用" hoverable={true}>
            <div>
                <div className="font-bold">count: {count}( 普通多次调用，每次都会重新渲染,且合并多次调用)</div>
                <Button className="my-12px" type="primary" onClick={handleClickCount}>更新数字</Button>
                <div className="font-bold max-w-300px flex-wrap whitespace-pre-wrap text-ellipsis break-words">name: {name}(普通多次调用，每次都会重新渲染,且合并多次调用)</div>
                <div>
                    <Button type="primary" onClick={handleClickName}>更新名称</Button>
                </div>
            </div>
        </Card>
        <Card title="useEffect的使用" hoverable={true} className="ml-12px">
            <div>
                <h2 className="font-bold text-16px mb-12px">useEffect(fun,arr)，fun是执行的函数，arr是执行依赖项目非必传</h2>
                <div className="font-bold mb-12px">普通数据类型：正常在arr中加入需要监听的state即可。</div>
                <div className="font-bold max-w-300px flex-wrap whitespace-pre-wrap text-ellipsis break-words">引用类型：不能直接监听整个引用对象，会导致每次渲染都执行fun，因为对象是引用类型，每次渲染都会生成一个新的引导，导致直接监听对象时每次渲染都执行。解决方法有两种，一种是限制arr中为对应属性，另一种是引入useMemo来限制对象。</div>
            </div>
        </Card>
        <Card title="useCallback的使用" hoverable={true} className="ml-12px">
            <div>
            </div>
        </Card>
        <Card title="useContex的使用" hoverable={true} className="ml-12px">
            <div>
                <h2 className="font-bold text-16px mb-12px">useContext(MyContext)，MyContext是创建的上下文对象</h2>
                <div className="font-bold mb-12px">使用useContext时，需要在组件树的顶部使用MyContext.Provider来提供上下文值，在需要使用上下文值的组件中使用useContext(MyContext)来获取上下文值。</div>
            </div>
        </Card>
    </div>
}

const Index = () => {
    const [childName, setChildName] = useState('')
    const [age, setAge] = useState(12)
    const [func, setFunc] = useState<any>(null)
    //useCallback优化函数的创建和传递，依赖项变化时才会重新创建
    //个人理解: useCallback会缓存其回调函数
    const handleClick = useCallback(() => {
        console.log('handleClick: ', handleClick == func);
        setChildName('son')
    }, [age])
    const onChange = (key: string) => {
        console.log(key);
    };

    const items: TabsProps['items'] = [
        {
            key: '1',
            label: 'Commonly Hook Demo',
            children: <HookDemo />,
        },
        {
            key: '2',
            label: 'useRef Demo',
            children: <DemoRef />,
        },
    ];
    return <div>
        <Tabs defaultActiveKey="1" items={items} onChange={onChange} />
    </div>
}

export default Index